package edu.bu.cs673.superkids123.children.view.games;

import javax.swing.*;

import edu.bu.cs673.superkids123.AbstractFrame;
import edu.bu.cs673.superkids123.AbstractNavigationView;
import edu.bu.cs673.superkids123.children.controller.games.ArcheryGameController;
import edu.bu.cs673.superkids123.children.controller.games.GameResultsController;
import edu.bu.cs673.superkids123.children.model.ArithmaticQuestion;
import edu.bu.cs673.superkids123.children.model.resources.ChildrenResource;
import edu.bu.cs673.superkids123.children.model.resources.ChildrenResourceProvider;
import edu.bu.cs673.superkids123.common.model.Child;

import java.awt.Color;
import java.awt.Font;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * The view for the archery game.
 * 
 * @author Noah Fredriks
 * 
 */
public class ArcheryGameView extends AbstractNavigationView implements MouseListener {

	public ArcheryGameView(AbstractFrame mainFrame) {
		super(mainFrame);
	}

	/**
	 * The default height of the game window
	 * 
	 */
	private static final int WINDOW_HEIGHT = 542;

	/**
	 * The default width of the game window
	 * 
	 */
	private static final int WINDOW_WIDTH = 740;

	/**
	 * A thread which runs the archeryGameCrosshair movement
	 * 
	 */
	private Thread crossThread;

	/**
	 * The archeryGameCrosshair object
	 * 
	 */
	private static final ArcheryGameCrosshair archeryGameCrosshair = new ArcheryGameCrosshair();

	/**
	 * An array which holds the five targets
	 */
	private ArcheryGameTarget[] targetArray;

	/**
	 * An array which holds the five questions
	 */
	private JLabel[] answerArray;

	/**
	 * a list of all the incorrect questions
	 */
	private ArrayList<ArithmaticQuestion> wrongQuestionList;

	/**
	 * the index of the correct target answer
	 */
	private int correctTarget;

	/**
	 * the current score
	 */
	private int gameScore;

	/**
	 * the current number of arrows left to shoot for the current question
	 */
	private int arrowsLeft;

	/**
	 * the game difficulty (speed of the archeryGameCrosshair) valid values are from 1 to 10
	 */
	private int difficulty;

	/**
	 * the number of questions answered correctly
	 */
	private int numRight;

	/**
	 * the flag signifying if numRight has been updated
	 */
	private boolean updatedPoint;

	/**
	 * the questions currently being displayed
	 */
	private ArithmaticQuestion curQuestion;

	/**
	 * the number of tries used to get all the questions correctly
	 */
	private int numPlayed;

	/**
	 * the number of questions
	 */
	private int numTotal;

	/**
	 * the score label
	 */
	private JLabel score;

	/**
	 * the difficulty label
	 */
	private JLabel diff;

	/**
	 * the arrows left label
	 */
	private JLabel arrows;

	/**
	 * the questions shot label
	 */
	private JLabel questions;

	/**
	 * The default number of arrows(chances) provided per question
	 */
	private static final int DEFAULT_ARROWS = 3;

	/**
	 * the layered pane which contains all the game elements
	 */
	private JLayeredPane layeredPane;

	/**
	 * This is the x offset for computing the origin for the next label
	 */
	private int offset;

	/**
	 * This is the y offset for computing the origin for the next label
	 */
	private int topset;

	/**
	 * This is the origin of the first label added
	 */
	private Point origin;

	/**
	 * A queue for all the questions not completed
	 */
	private Queue<ArithmaticQuestion> questionQueue;
	
	/**
	 * A list of names to call the player.
	 */
	private List<String> nameList;
	
	/**
	 * True if the game ended and results have been posted.
	 */
	private boolean gameOver;

	private void resetOrigin() {
		origin = new Point(0, 0);
		origin.y = topset;
	}

	/**
	 * Create and set up targets.
	 * 
	 * @param origin
	 *            the position of the upper left corner of target placement
	 * @param correct
	 *            a flag which tells whether the target is beneath the correct answer
	 * @param i
	 *            the index of the target(from left to right on screen)
	 * @return an array pointer to the target object created
	 * 
	 */
	private JLabel createTargetLabel(Point origin, int i) {
		targetArray[i] = new ArcheryGameTarget();
		targetArray[i].setVerticalAlignment(JLabel.TOP);
		targetArray[i].setHorizontalAlignment(JLabel.CENTER);
		targetArray[i].setBounds(origin.x, origin.y, 160, 200);
		return targetArray[i];
	}

	/**
	 * Create and set up answer labels.
	 * 
	 * @param origin
	 *            the position of the upper left corner of target placement
	 * @param int the index of the answer
	 * @return a label containing the new answer
	 * 
	 */
	private JLabel createAnswerLabel(Point origin, int i) {
		answerArray[i] = new JLabel("");
		answerArray[i].setVerticalAlignment(JLabel.TOP);
		answerArray[i].setHorizontalAlignment(JLabel.CENTER);
		return answerArray[i];
	}

	/**
	 * Update the answer labels, needs to be called for each not correctly shot answer in the list
	 */
	private void updateAnswers() {
		if (!questionQueue.isEmpty()) {
			updatedPoint = false;
			curQuestion = questionQueue.remove();

			resetOrigin();
			
			correctTarget = (curQuestion.getCorrectAnswerLocation() - 1);

			for (int i = 0; i < answerArray.length; i++) {
				targetArray[i].setCorrect(curQuestion.getCorrectAnswerLocation() == (i + 1));
				Font answerFont = new Font("", Font.BOLD, 32);
				answerArray[i].setFont(answerFont);
				answerArray[i].setText(Integer.toString(curQuestion.getOption(i + 1)));
				answerArray[i].setBounds(origin.x, (origin.y - 40), 160, 40);
				answerArray[i].setVerticalAlignment(JLabel.TOP);
				answerArray[i].setHorizontalAlignment(JLabel.CENTER);

				layeredPane.add(createTargetLabel(origin, i), new Integer(i + 1));
				origin.x += offset;
			}
		} else {
			gameOver = true;
			getMainFrame().getController(ArcheryGameController.class).endGame(gameOver);
		}
	}

	/**
	 * Starts the initial archeryGameCrosshair thread for game commencement.
	 * 
	 */
	private void playGame() {
		updateAnswers();

		crossThread = new Thread(archeryGameCrosshair);

		crossThread.start();

	}

	/**
	 * Contains all the logic necessary to get the score of the arrow fired. Then calls updateHUD()
	 * to display it.
	 * 
	 */
	private void score() {
		int x = archeryGameCrosshair.getX() + 30;
		int targetHit;
		int posOnTarget;
		boolean hit = false;
		arrowsLeft--;

		// Determine position relative to nearest(targetHit) target
		targetHit = x / offset;
		posOnTarget = x % offset;

		// Determine if a target was hit
		if (30 <= posOnTarget && posOnTarget < 130) {
			hit = true;
		}

		// Determine if the correct target was hit
		if (hit && targetHit == correctTarget) {

			if (!updatedPoint) {
				numRight++;
				getMainFrame().getController(GameResultsController.class).correct++;
				updatedPoint = true;
			}

			// Increase score based on region hit

			gameScore += 10; // white
			if (40 <= posOnTarget && posOnTarget < 120) {
				gameScore += 10; // black
			}
			if (50 <= posOnTarget && posOnTarget < 110) {
				gameScore += 10; // blue
			}
			if (60 <= posOnTarget && posOnTarget < 100) {
				gameScore += 10; // red
			}
			if (70 <= posOnTarget && posOnTarget < 90) {
				gameScore += 10; // yellow
			}
		}

		updateHUD();

	}

	/**
	 * Updates the head unit display to show the latest game statistics
	 * 
	 */
	private void updateHUD() {
		score.setText(String.valueOf(gameScore));
		score.repaint();
		questions.setText(String.valueOf(numRight) + "/" + String.valueOf(numTotal));
		questions.repaint();
		arrows.setText(String.valueOf(arrowsLeft));
		arrows.repaint();
	}

	/**
	 * Creates the head unit display for the archery game. This holds all the game statistics.
	 * Method is a refactored portion of the constructor in order to improve readability.
	 * 
	 * @return JPanel of the game statistics
	 */
	private JPanel createHUD() {

		JPanel whole = new JPanel(new BorderLayout());
		JPanel stats = new JPanel(new GridLayout(1, 8));
		stats.setBackground(Color.BLACK);

		Font statLFont = new Font("", Font.PLAIN, 12);
		Font statFont = new Font("", Font.BOLD, 28);

		JLabel scoreL = new JLabel("Score:  ");
		scoreL.setFont(statLFont);
		scoreL.setForeground(Color.WHITE);
		scoreL.setHorizontalAlignment(JLabel.RIGHT);
		JLabel diffL = new JLabel("Difficulty:  ");
		diffL.setFont(statLFont);
		diffL.setForeground(Color.WHITE);
		diffL.setHorizontalAlignment(JLabel.RIGHT);
		JLabel correctL = new JLabel("Ratio Correct:  ");
		correctL.setFont(statLFont);
		correctL.setForeground(Color.WHITE);
		correctL.setHorizontalAlignment(JLabel.RIGHT);
		JLabel arrowsL = new JLabel("Arrows:  ");
		arrowsL.setFont(statLFont);
		arrowsL.setForeground(Color.WHITE);
		arrowsL.setHorizontalAlignment(JLabel.RIGHT);

		score = new JLabel("0");
		score.setFont(statFont);
		score.setForeground(Color.YELLOW);
		score.setHorizontalAlignment(JLabel.LEFT);
		diff = new JLabel(String.valueOf(archeryGameCrosshair.getDifficulty()));
		diff.setFont(statFont);
		diff.setForeground(Color.YELLOW);
		diff.setHorizontalAlignment(JLabel.LEFT);
		questions = new JLabel("0/" + numTotal);
		questions.setFont(statFont);
		questions.setForeground(Color.YELLOW);
		questions.setHorizontalAlignment(JLabel.LEFT);
		arrows = new JLabel(String.valueOf(DEFAULT_ARROWS));
		arrows.setFont(statFont);
		arrows.setForeground(Color.YELLOW);
		arrows.setHorizontalAlignment(JLabel.LEFT);

		stats.add(scoreL);
		stats.add(score);
		stats.add(diffL);
		stats.add(diff);
		stats.add(correctL);
		stats.add(questions);
		stats.add(arrowsL);
		stats.add(arrows);
		whole.add(stats, BorderLayout.CENTER);

		return whole;
	}

	public void mousePressed(MouseEvent e) {
		if (archeryGameCrosshair.isMoving()) {
			archeryGameCrosshair.stop();

			score();

			if (arrowsLeft <= 0) {
				wrongQuestionList.add(curQuestion);
				
				for (int i = 0; i < answerArray.length; i++) {
					if (i != correctTarget) {
						answerArray[i].setForeground(Color.RED);
						answerArray[i].repaint();
					}
				}
			}

		} else {
			if (arrowsLeft <= 0) {
				for (int i = 0; i < answerArray.length; i++) {
					answerArray[i].setForeground(Color.YELLOW);
					answerArray[i].repaint();
				}

				try {
					// Give a little more time so user can read highlighted answer
					Thread.sleep(100);
				} catch (InterruptedException iE) {
					System.err.println("ArcheryGameView: " + iE.getMessage());
				}
				arrowsLeft = DEFAULT_ARROWS;
				numPlayed++;
				arrows.setText(String.valueOf(arrowsLeft));
				arrows.repaint();
				updateAnswers();

				updateUIElements();

			}
			crossThread = new Thread(archeryGameCrosshair);
			crossThread.start();
		}
	}

	// Non-functional but included because of interface
	public void mouseExited(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseReleased(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}

	@Override
	public String getTitle() {
		if (curQuestion == null) {
			return "Loading...";
		} else if (numPlayed >= numTotal) {
			return "Finished!";
		} else {
			return nameList.get(numPlayed) + Integer.toString(curQuestion.getA()) + " + " +
				Integer.toString(curQuestion.getB());
		}
	}

	@Override
	public String getBorderTitle() {
		return "Archery Game";
	}

	@Override
	protected JComponent layout() {
		JPanel win = new JPanel();
		win.addMouseListener(this);

		setAvatarVisible(false);
		setParentsButtonVisible(false);
		setTitleBorderVisible(false);

		Child child = (Child) authController.getCurrentUser();
		
		// Initialize private data members
		gameScore = 0;
		numRight = 0;
		numPlayed = 0;
		gameOver = false;
		arrowsLeft = DEFAULT_ARROWS;
		difficulty = child.getAge();
		archeryGameCrosshair.setDifficulty(difficulty);
		wrongQuestionList = new ArrayList<ArithmaticQuestion>();
		origin = new Point(0, 0);
		offset = 140;
		topset = 180;
		
		nameList = new ArrayList<String>();
		nameList.add(child.getName() + ", shoot ");
		nameList.add("Great!  Now, try for ");
		nameList.add("Awesome!  Bowman, target ");
		nameList.add("Terrific!  Sharpshooter, aim for ");
		nameList.add("Nice work!  Archer, get ");
		nameList.add("Good job!  How about ");
		nameList.add("Awesome!  Try to hit ");
		nameList.add("Wow!  Go for ");
		nameList.add("Almost there!  Blast ");
		nameList.add("Last one!  Barrage ");

		win.setLayout(new BoxLayout(win, BoxLayout.PAGE_AXIS));

		// Create and set up the layered pane.
		layeredPane = new JLayeredPane();
		layeredPane.setPreferredSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));

		// Add several overlapping, colored labels to the layered pane
		// using absolute positioning/sizing.
		ChildrenResourceProvider provider = ChildrenResourceProvider.getInstance();
		ImageIcon bg = provider.getImage(ChildrenResource.imageField);
		JLabel bgLabel = new JLabel(bg);
		bgLabel.setVerticalAlignment(JLabel.TOP);
		bgLabel.setHorizontalAlignment(JLabel.CENTER);
		bgLabel.setForeground(Color.black);
		bgLabel.setBorder(BorderFactory.createLineBorder(Color.black));
		bgLabel.setBounds(origin.x, origin.y, WINDOW_WIDTH, WINDOW_HEIGHT);
		layeredPane.add(bgLabel, new Integer(0));
		origin.y = topset;

		questionQueue = new LinkedList<ArithmaticQuestion>();
		numTotal = 10; // TODO

		for (int i = 0; i < numTotal; i++) {
			questionQueue.add(new ArithmaticQuestion(child.getAge(), 5)); // TODO remove magic number 5 targets
		}

		answerArray = new JLabel[5];
		targetArray = new ArcheryGameTarget[5];
		for (int i = 0; i < 5; i++) {

			layeredPane.add(createAnswerLabel(origin, i), new Integer(i + 2));

			layeredPane.add(createTargetLabel(origin, i), new Integer(i + 1));
			origin.x += offset;
		}

		layeredPane.add(archeryGameCrosshair, new Integer(7), 0);
		// Add layered pane to this JPanel.
		win.add(layeredPane);
		win.add(createHUD());

		playGame();

		return win;
	}
	
	@Override
	public boolean cleanupView() {
		
		if (gameOver == false) {
			getMainFrame().getController(ArcheryGameController.class).endGame(gameOver);
		}
		
		return true;
	}

}
